home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue40 / Threads / HVSignalList.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-11-03  |  8.1 KB  |  282 lines

  1. unit HVSignalList;
  2. //
  3. // Written by Hallvard Vassbotn, hallvard@falcon.no
  4. //
  5. // Based on source code Copyright (c) 1998 by Reuters Group PLC
  6. // Reproduction and/or distribution of source code or DCUs strictly prohibited.
  7. //
  8. // For publication in The Delphi Magazine only
  9. //
  10. interface
  11.  
  12. uses
  13.   Windows,
  14.   Classes,
  15.   HVSyncObjs
  16.   ;
  17.  
  18. type
  19.   // We can be waiting for:
  20.   TCustomSignal = class(TObject)
  21.   private
  22.     FOnTrigger: TNotifyEvent;
  23.   protected
  24.     function GetHandle: THandle; virtual; abstract;
  25.   public
  26.     constructor Create(anOnTrigger: TNotifyEvent);
  27.     procedure Trigger; virtual;
  28.     property Handle: THandle read GetHandle;
  29.   end;
  30.  
  31.   // - a TThread ending
  32.   TThreadSignal = class(TCustomSignal)
  33.   private
  34.     FThread: TThread;
  35.   protected
  36.     function GetHandle: THandle; override;
  37.   public
  38.     constructor Create(aThread: TThread; anOnTrigger: TNotifyEvent);
  39.     property Thread: TThread read FThread;
  40.   end;
  41.  
  42.   // - a raw Handle returned by the Windows kernel
  43.   THandleSignal = class(TCustomSignal)
  44.   private
  45.     FHandle: THandle;
  46.   protected
  47.     function GetHandle: THandle; override;
  48.   public
  49.     constructor Create(aHandle: THandle; anOnTrigger: TNotifyEvent);
  50.   end;
  51.  
  52.   // - a syncrynization object derived from THandleObject
  53.   TObjectSignal = class(TCustomSignal)
  54.   private
  55.     FHandleObject: THandleObject;
  56.   protected
  57.     function GetHandle: THandle; override;
  58.   public
  59.     constructor Create(aHandleObject: THandleObject; anOnTrigger: TNotifyEvent);
  60.     property HandleObject: THandleObject read FHandleObject;
  61.   end;
  62.  
  63.   TSignalList = class(TObject)
  64.   private
  65.     FObjs           : TWOHandleArray;
  66.     FList           : TList;
  67.     FMsgWakeupMask  : longint;
  68.     FIgnoreMessages : boolean;
  69.     FWaitForAll     : boolean;
  70.     procedure TriggeredIndex(Index: integer);
  71.   public
  72.     constructor Create;
  73.     destructor Destroy; override;
  74.  
  75.     // Adding new triggers for handles, handleobjects and threads
  76.     // Currently no support for removing triggers...
  77.     procedure AddSignal(aSignal: TCustomSignal);
  78.  
  79.     function WaitOne(WaitTime: DWORD; var Index: integer): TWaitResult;
  80.     function WaitOneAndTrigger(WaitTime: DWORD): TWaitResult;
  81.     function WaitUntil(WaitTime: DWORD; WaitResultStop: TWaitResults): TWaitResult;
  82.     property IgnoreMessages: boolean read FIgnoreMessages write FIgnoreMessages;
  83.     property WaitForAll: boolean read FWaitForAll write FWaitForAll;
  84.     property MsgWakeupMask: longint read FMsgWakeupMask write FMsgWakeupMask;
  85.   end;
  86.  
  87. { Utility routines }
  88.  
  89. function WaitForSingleHandleObject(Timeout: DWORD; WaitObject: THandleObject): TWaitResult;
  90. function WaitForAnyHandleObject (Timeout: DWORD; var SignaledObject: THandleObject; WaitObjects: array of THandleObject): TWaitResult;
  91. function WaitForAllHandleObjects(Timeout: DWORD; var SignaledObject: THandleObject; WaitObjects: array of THandleObject): TWaitResult;
  92.  
  93. implementation
  94.  
  95. uses
  96.   SysUtils,
  97.   HVUtils
  98.   ;
  99.  
  100. { TCustomSignal }
  101.  
  102. constructor TCustomSignal.Create(anOnTrigger: TNotifyEvent);
  103. begin
  104.   inherited Create;
  105.   FOnTrigger := anOnTrigger;
  106. end;
  107.  
  108. procedure TCustomSignal.Trigger;
  109. begin
  110.   if Assigned(FOnTrigger) then
  111.     FOnTrigger(Self);
  112. end;
  113.  
  114. { TThreadSignal }
  115.  
  116. constructor TThreadSignal.Create(aThread: TThread; anOnTrigger: TNotifyEvent);
  117. begin
  118.   inherited Create(anOnTrigger);
  119.   FThread := aThread;
  120. end;
  121.  
  122. function TThreadSignal.GetHandle: THandle;
  123. begin
  124.   Result := Thread.Handle;
  125. end;
  126.  
  127. { THandleSignal }
  128.  
  129. constructor THandleSignal.Create(aHandle: THandle; anOnTrigger: TNotifyEvent);
  130. begin
  131.   inherited Create(anOnTrigger);
  132.   FHandle := aHandle;
  133. end;
  134.  
  135. function THandleSignal.GetHandle: THandle;
  136. begin
  137.   Result := FHandle;
  138. end;
  139.  
  140. { TObjectSignal }
  141.  
  142. constructor TObjectSignal.Create(aHandleObject: THandleObject; anOnTrigger: TNotifyEvent);
  143. begin
  144.   inherited Create(anOnTrigger);
  145.   FHandleObject := aHandleObject;
  146. end;
  147.  
  148. function TObjectSignal.GetHandle: THandle;
  149. begin
  150.   Result := HandleObject.Handle;
  151. end;
  152.  
  153. { TSignalList }
  154.  
  155. constructor TSignalList.Create;
  156. begin
  157.   inherited Create;
  158.   FList := TList.Create;
  159.   // See MsgWaitForMultipleObjects in help for list of possible values
  160.   FMsgWakeupMask := QS_AllInput;
  161. end;
  162.  
  163. destructor TSignalList.Destroy;
  164. begin
  165.   FreeOwningTList(FList);
  166.   inherited Destroy;
  167. end;
  168.  
  169. procedure TSignalList.AddSignal(aSignal: TCustomSignal);
  170. begin
  171.   // Check that we are not passing any limits (currently 64!)
  172.   if FList.Count >= MAXIMUM_WAIT_OBJECTS then
  173.     raise Exception.Create('Too many wait-objects!');
  174.  
  175.   // Update the low-level array with this new handle
  176.   FObjs[FList.Count] := aSignal.Handle;
  177.  
  178.   // Add the thread event to the list
  179.   FList.Add(aSignal);
  180. end;
  181.  
  182. procedure TSignalList.TriggeredIndex(Index: integer);
  183. begin
  184.   // Use assertions to guarantee correct code while debugging and fast release code
  185.   Assert((Index >= 0) and (Index < FList.Count));
  186.   Assert(TObject(FList[Index]) is TCustomSignal);
  187.   Assert(FObjs[Index] = TCustomSignal(FList[Index]).Handle);
  188.  
  189.   // Get the Signal associated with this index and trigger the event
  190.   TCustomSignal(FList.List^[Index]).Trigger;
  191. end;
  192.  
  193. function TSignalList.WaitOne(WaitTime: DWORD; var Index: integer): TWaitResult;
  194. // We use the blocking function MsgWaitForMultipleObjects to wait for any
  195. // message in the message queue or any signaled object from any of the
  196. // other running threads in this process. See WINAPI32.HLP for details.
  197. var
  198.   WaitResult: DWORD;
  199. begin
  200.   // This call will block and use 0% CPU time until:
  201.   // - A message arrives in the message queue, or
  202.   // - Any of the object handles in the Objs array become signaled
  203.   if IgnoreMessages
  204.   then WaitResult :=    WaitForMultipleObjects(FList.Count, @FObjs, WaitForAll, WaitTime)
  205.   else WaitResult := MsgWaitForMultipleObjects(FList.Count,  FObjs, WaitForAll, WaitTime, MsgWakeupMask);
  206.  
  207.   // Index is only valid when Result = wrSignaled
  208.   Index := WaitResult - WAIT_OBJECT_0;
  209.  
  210.   // Convert from WAIT_ returncode to TWaitResult
  211.   case WaitResult of
  212.     WAIT_ABANDONED: Result := wrAbandoned;
  213.     WAIT_TIMEOUT  : Result := wrTimeout;
  214.     WAIT_FAILED   : Result := wrError;
  215.     else
  216.       if WaitResult = DWORD(WAIT_OBJECT_0 + FList.Count)
  217.       then Result := wrMessage
  218.       else Result := wrSignaled  // WAIT_OBJECT_0 .. WAIT_OBJECT_0+(FList.Count-1)
  219.   end;
  220. end;
  221.  
  222. function TSignalList.WaitOneAndTrigger(WaitTime: DWORD): TWaitResult;
  223. var
  224.   Index: integer;
  225. begin
  226.   Result := WaitOne(WaitTime, Index);
  227.   if Result = wrSignaled then
  228.     TriggeredIndex(Index);
  229. end;
  230.  
  231. function TSignalList.WaitUntil(WaitTime: DWORD; WaitResultStop: TWaitResults): TWaitResult;
  232. begin
  233.   repeat
  234.     Result := WaitOneAndTrigger(WaitTime);
  235.   until (Result in WaitResultStop);
  236. end;
  237.  
  238. { Utility routines }
  239.  
  240. function WaitForHandleObjects(Timeout: DWORD; var SignaledObject: THandleObject; WaitObjects: array of THandleObject; WaitForAll: boolean): TWaitResult;
  241. var
  242.   SignalList : TSignalList;
  243.   Index      : integer;
  244.   i          : integer;
  245. begin
  246.   SignalList := TSignalList.Create;
  247.   try
  248.     for i := Low(WaitObjects) to High(WaitObjects) do
  249.       SignalList.AddSignal(TObjectSignal.Create(WaitObjects[i], nil));
  250.  
  251.     SignalList.WaitForAll := WaitForAll;
  252.     SignalList.IgnoreMessages := true;
  253.  
  254.     Result := SignalList.WaitOne(Timeout, Index);
  255.  
  256.     if Result = wrSignaled
  257.     then SignaledObject := WaitObjects[Index]
  258.     else SignaledObject := nil;
  259.   finally
  260.     SignalList.Free;
  261.   end;
  262. end;
  263.  
  264. function WaitForSingleHandleObject(Timeout: DWORD; WaitObject: THandleObject): TWaitResult;
  265. var
  266.   SignaledObject: THandleObject;
  267. begin
  268.   Result := WaitForHandleObjects(Timeout, SignaledObject, [WaitObject], false);
  269. end;
  270.  
  271. function WaitForAnyHandleObject(Timeout: DWORD; var SignaledObject: THandleObject; WaitObjects: array of THandleObject): TWaitResult;
  272. begin
  273.   Result := WaitForHandleObjects(Timeout, SignaledObject, WaitObjects, false);
  274. end;
  275.  
  276. function WaitForAllHandleObjects(Timeout: DWORD; var SignaledObject: THandleObject; WaitObjects: array of THandleObject): TWaitResult;
  277. begin
  278.   Result := WaitForHandleObjects(Timeout, SignaledObject, WaitObjects, true);
  279. end;
  280.  
  281. end.
  282.